import java.util.*;
import java.math.*;

public class Logic
{
    //  numberOfVariables are assumed to be two variables for the time being.
    public int numberOfVariables;
    
    //  The arraylist containing the input tuples (obviously)
    ArrayList<Tuple> inputOutput = new ArrayList<Tuple>();
    /* 
        fullArray consists of the full logical array for all possible inputs (where the output is either,
        '1' or dont care),
        the output is the second element in the tuple. If the output is unspecified for a certain input
        it is considered to be '2' (to symbolize don't care term).*/
    public ArrayList<Tuple> fullArray = new ArrayList<Tuple>();

   // public ArrayList<StringTuple> stringTs = new ArrayList<StringTuple>();

    // OLD NOT USED; public ArrayList<ArrayList<String>> numberOf1sArray = new ArrayList<ArrayList<String>>();
    
    //  fillArray will assign all ouputs to fullArray, see fullArray.
    
    public void setNumberOfVariables(int n)
    {
        numberOfVariables = n;
        System.out.println("Number of variables has been set to: " + n);
    }
    
    public void setInputOutput(ArrayList<Tuple> io)
    {
        inputOutput = io;
        System.out.println("Input/Output has been set");
	/*for (int i=0; i<io.size(); i++){
		io.get(i).print();
	}*/
    }
     
    public void fillArray()
    {
        int j = 0;
        for (int i = 0; i < (Math.pow(2, numberOfVariables)); ++i)
        {
            if (i == binaryToInt(inputOutput.get(j).get(1)))
            {
                if (inputOutput.get(j).get(2).get(0) == 1)
                {   
                    fullArray.add(i, new Tuple());
                    fullArray.get(i).get(2).add(1);
                    fullArray.get(i).set(inputOutput.get(j).get(1), 1);
                    if (!(j == inputOutput.size()-1))
                        ++j;
                }
            }
            else 
            {
                fullArray.add(i, new Tuple());
                fullArray.get(i).get(2).add(2);
                fullArray.get(i).set(intToBinary(i),1);
            }
        }
        System.out.println("---------------------");
        for (int k = 0; k < fullArray.size(); ++k)
         System.out.println(fullArray.get(k).get(1));
    }   
    public ArrayList<Integer> intToBinary(int n)
    {
        ArrayList<Integer> list = new ArrayList<Integer>();
		for (int a = 0; a < numberOfVariables; a++){
			list.add(0);
		}

		String s = Integer.toBinaryString(n);
		String[] sl = s.split("");
		int length = 0;
		try{
			while(true){
				length++;
				String test = sl[length];
			}
		} 
		catch (Exception e){
			length--;
		}
		  
		int i = list.size()-1;
		while (length > 0){
			Integer k = new Integer(sl[length]);
			list.set(i,k);
			i--;
			length--;
		}
		return list;
    }
    
    
    public void getPrimeImplicants()
    {
        int position = 0;
        boolean changed = true;
        while (changed)
        {
            changed = false;
            for(int i = 0; i < (Math.pow(2, numberOfVariables)); ++i)
                for(int k = 0; k < (Math.pow(2, numberOfVariables)); ++k)
                {
                    if (!(k == i))
                    {
                        position = canCombine(fullArray.get(i).get(1), fullArray.get(k).get(1));
                        if (!(position == -1) && (!(fullArray.get(i).get(1) == fullArray.get(k).get(1))))
                        {                             
                            combinePositions(i,k,position);
                            changed = true;
                        }
                    }
                }    
        }   
    }
    
    //canCombine checks if two input/output pairs can be combined, returns the position
    //they can be combined at, returns -1 if combinations is impossible.
    private int canCombine(ArrayList<Integer> arrayList1,ArrayList<Integer> arrayList2)
    {
        int difference = 0;
        int position = -1;
        for(int i = 0; i < numberOfVariables; ++i)
            if (!(arrayList1.get(i) == arrayList2.get(i)))
            {
                if (++difference == 2)
                    return -1;
                position = i;
            }
        return position;
    }
    
    /*
        combinePositions combines two tuples by exchanging both of their inputs value at position position
        with '2' (indicates it's dont care position) and by combining their output, if either of the two outputs
        = 1 then both will get output 1, else will be '2'.
        */
    private void combinePositions(int i, int k, int position)
    {
        fullArray.get(i).get(1).set(position,2);
        fullArray.get(k).get(1).set(position,2);
        if ((fullArray.get(i).get(2).get(0) == 1) || (fullArray.get(k).get(2).get(0) == 1))
        {
            fullArray.get(i).get(2).set(0,1);
            fullArray.get(k).get(2).set(0,1);
        }
        
        else
        {
             fullArray.get(i).get(2).set(0,2);
             fullArray.get(k).get(2).set(0,2);        
        
        }
        
    }
    
    public void writeItOut()
    {
        String s = "";
        for (int i = 0; i < fullArray.size(); ++i)
        {
            if (fullArray.get(i).get(2).get(0) == 1)
            {
                for (int k = 0; k < numberOfVariables; ++k)
                {
                    if (fullArray.get(i).get(1).get(k) == 1)
                        s = s + "a" + k;
                    if (fullArray.get(i).get(1).get(k) == 0)
                        s = s + "a" + k + "'";
                } 
                s = s + "  +  ";   
            }                                     
        }
        System.out.println(s);
        
    }
    /* NOTE OLD FUNCTION UNUSED AT CURRENT MOMENT
    
    divideByNumbsOf1s should be called with fullArray as input.
    private void divideByNumsOf1s(ArrayList<Integer> array)
    {
        for (int i = 0; i < (2^numberOfVariables);++i)
        {
            if (array.get(i) == 1) 
            {
                numberOf1sArray.get(Integer.bitCount(i)).add(Integer.toBinaryString(i));
            }      
        
        }
    
    } */
    
    /*
        binaryToInt will tak a binary number in the form of an arrayList where every cell contains either
        '0' or '1' and return it in the form of an int.*/
    private int binaryToInt(ArrayList<Integer> binaryInDisguise)
    {
        int sum = 0;
        int k = 1;
        for (int i = (numberOfVariables-1); i > (-1);i--)
        {
            if (binaryInDisguise.get(i) == 1) 
            { 
                sum = sum + k;
            }    
            k = k * 2;
        }
        return sum;
    }
      
    
} 
